Skip to content

feat(joke-bot): sandbox:false (HTTP writeback, no Daytona box) + channel guard#84

Merged
khaliqgant merged 4 commits into
mainfrom
fix/joke-bot-channel-guard
Jun 21, 2026
Merged

feat(joke-bot): sandbox:false (HTTP writeback, no Daytona box) + channel guard#84
khaliqgant merged 4 commits into
mainfrom
fix/joke-bot-channel-guard

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Jun 21, 2026

Copy link
Copy Markdown
Member

Makes joke-bot a true lightweight reply bot — no Daytona sandbox — and fixes it replying outside its configured channel.

What changed

  • sandbox: false (persona). The handler answers with one ctx.llm.complete call and runs in the persona runner — no Daytona box, no cold start (ms instead of minutes).
  • Slack writeback over HTTP. Bump @relayfile/relay-helpers 0.3.42 → ^0.4.1 (resolves @relayfile/adapter-core 0.4.2, relayfile-adapters#221): writeJsonFile routes to RelayFileClient (HTTP, via the injected RELAYFILE_TOKEN/RELAYFILE_URL) when there's no FS mount, and to the FS mount when there is one (unchanged for harness/sandbox agents). cloud injects those vars on the sandbox:false path (cloud#2412).
  • Channel guard (agent.ts). The slack trigger wakes across channels (broad slack scope feeds the wake-path match; trigger match isn't enforced cloud-side yet), so the handler now drops any @mention whose channel ≠ SLACK_CHANNEL. Fixes joke-bot answering in #epic-relayfile-migration.

Scope note

The relay-helpers bump is repo-wide — other agents pick up 0.4.1 on their next deploy (API-compatible; joke-bot typechecks clean). Nothing else is redeployed by this PR.

Why sandbox:false is safe here

joke-bot reads no provider data / VFS and never calls ctx.harness.run. Agents that need the FS mount or a harness CLI must stay sandbox: true.

🤖 Generated with Claude Code

joke-bot replied to an @mention in #epic-relayfile-migration — a channel it
should not answer in. Its slack `message.created` trigger wakes across channels
(the broad slack scope feeds the wake-path match), and the trigger-level
`match: '@mention'` gate is not enforced cloud-side yet (AgentWorkforce/cloud#2411
merged; pending deploy). So any @mention in any channel woke it and it replied.

Add a handler-level channel guard in handleSlackMention: resolve SLACK_CHANNEL,
normalize the event channel `id__name` → `id`, and skip unless they match. Now
joke-bot only ever replies in the configured channel, regardless of where the
wake fires. Once #2411 deploys, the trigger `match` will also stop the wake.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@khaliqgant, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 55 minutes and 34 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c01cf985-24b3-4a2f-9964-341ba616de55

📥 Commits

Reviewing files that changed from the base of the PR and between 891c159 and 841f221.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • joke-bot/agent.ts
  • joke-bot/persona.ts
  • package.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/joke-bot-channel-guard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9890c40985

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread joke-bot/agent.ts Outdated
// would answer @mentions in ANY channel. Normalize `id__name` → `id`.
const want = input(ctx, 'SLACK_CHANNEL');
const chanId = channel.split('__')[0];
if (want && chanId !== want) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require configured channel before allowing replies

When SLACK_CHANNEL cannot be resolved, this guard is bypassed because it only returns on want && chanId !== want; the handler then continues and replies in the event's channel. Since the Slack trigger can still wake across channels, a missing or miswired channel input leaves the original cross-channel reply behavior intact instead of failing closed like the relay and cron paths do.

Useful? React with 👍 / 👎.

@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer could not complete review for #84 in AgentWorkforce/agents.
The review harness exited with code 1.
No review was posted; this needs operator attention.

khaliqgant and others added 2 commits June 21, 2026 22:20
…0.4.1)

joke-bot no longer needs a Daytona box. Reply generation is ctx.llm.complete
(one inference call) and the Slack writeback now goes over the relayfile HTTP API
instead of the FS mount — so a box/mount is unnecessary.

- persona: sandbox: false (handler runs in the persona runner, ms; no cold start).
- bump @relayfile/relay-helpers 0.3.42 → ^0.4.1, which resolves @relayfile/adapter-core
  0.4.2 (relayfile-adapters#221): writeJsonFile routes to RelayFileClient (HTTP,
  over the injected RELAYFILE_TOKEN/URL) when no FS mount is configured, and to
  the FS mount when one is (unchanged for harness agents). cloud already injects
  RELAYFILE_TOKEN/URL on the sandbox:false path (cloud#2412).

Repo-wide dep bump: other agents pick up relay-helpers 0.4.1 on their next deploy
(API-compatible; joke-bot typechecks clean). Channel guard from the prior commit
on this branch is retained.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pairs with the relay-helpers 0.4.1 bump on this branch — joke-bot runs in the
persona runner with no Daytona box; the Slack writeback goes over the relayfile
HTTP API.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@khaliqgant khaliqgant changed the title fix(joke-bot): only reply in the configured channel feat(joke-bot): sandbox:false (HTTP writeback, no Daytona box) + channel guard Jun 21, 2026
Addresses Codex P2 on the slack mention path: the channel guard only
returned on `want && chanId !== want`, so a missing/miswired SLACK_CHANNEL
fell through and replied in the event's channel. Now returns when `want`
is unset, matching the relay and cron paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@khaliqgant khaliqgant merged commit 873e0c5 into main Jun 21, 2026
2 checks passed
@khaliqgant khaliqgant deleted the fix/joke-bot-channel-guard branch June 21, 2026 20:25
@agent-relay-code

Copy link
Copy Markdown
Contributor

ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed.

PR #84 Review — fix/joke-bot-channel-guard

Summary

This PR makes three coordinated changes to joke-bot:

  1. agent.ts — adds a channel guard in handleSlackMention that only replies in the configured SLACK_CHANNEL, normalizing the incoming id__nameid.
  2. persona.ts — flips sandbox: truesandbox: false, with a comment explaining the Slack writeback now routes over the relayfile HTTP API (no FS mount needed).
  3. package.json / package-lock.json — bumps @relayfile/relay-helpers 0.3.42^0.4.1 (pulls adapter-core 0.4.2), the dependency that enables the mount-less HTTP writeback.

The three changes are internally consistent: sandbox:false removes the relayfile FS mount, and the relay-helpers ≥0.4.1 bump is what lets writeJsonFile fall back to the HTTP client when no mount exists.

Verification (run against the current checkout)

  • npm ci — succeeds → package.json and package-lock.json are in sync.
  • npm run typecheck (tsc --noEmit) — passes.
  • npm test (tsc build + node --test tests/*.test.mjs) — 125/125 pass when run in a clean environment.
  • agentworkforce persona compile joke-bot/persona.ts — compiles cleanly with sandbox:false.

Note on a transient test failure: in this harness sandbox, tests/hn-monitor.test.mjs initially failed because the harness injects process.env.SLACK_CHANNEL=C0ALQ06AAUT (the reviewer agent's own deploy context), and hn-monitor's input() helper prefers process.env over persona inputs, overriding the test's 'C123'. Re-running with that env var unset (env -u SLACK_CHANNEL npm test) yields 125/125 green. This is a sandbox environment artifact, not a PR regression — hn-monitor is untouched by this PR and real CI has no such injected var. No code change made.

Findings (left as comments — not auto-edited)

1. Channel guard: asymmetric normalization of want vs chanIdjoke-bot/agent.ts:143-145

const want = input(ctx, 'SLACK_CHANNEL');
const chanId = channel.split('__')[0];
if (want && chanId !== want) { ... return; }

The incoming channel is normalized (split('__')[0]) but want is not. If the SLACK_CHANNEL picker ever resolves to an id__name form (the same form being defended against on the inbound side), want would carry the __name suffix and chanId !== want would always be true, causing the bot to silently never reply. The current evidence (capabilities.channels: ['C0AD7UU0J1G'] is a bare id, and paths: ['/slack/channels/${SLACK_CHANNEL}/**']) suggests want is a bare id today, so this works — but the normalization is one-sided. Consider normalizing want the same way (input(...)?.split('__')[0]) for symmetry/robustness. This is a fail-safe (skip on mismatch) behavior decision, so I'm leaving it for human judgment rather than editing.

2. Stale module docstring in persona.tsjoke-bot/persona.ts:14-19
The top-of-file doc comment still describes the old design: "It is also the harness test vehicle: the joke is generated via ctx.harness.run (NOT ctx.llm.complete)" and "Reply surface is Slack writeback (slackClient().post)". This now contradicts the changed code — the handler uses ctx.llm.complete and runs with sandbox:false. The inline comment at lines 29-34 was updated, but this header block was not. Comment-only (no runtime effect); recommend the author align the header prose with the new behavior. Left unchanged to keep this review scoped to the diff and avoid prose rewrites.

Addressed comments

  • No bot or human review comments were present in the provided PR metadata (.workforce/context.json contains no comments/reviews, and there is no comments file). Nothing to address.

Advisory Notes

  • The overrides block in package.json pins @relayfile/adapter-linear to 0.3.11, while the new relay-helpers@0.4.1 declares @relayfile/adapter-linear: ^0.4.1. npm ci resolves cleanly (the override wins, installing 0.3.11), so this is not a build break and it predates this PR (the old relay-helpers@0.3.42 required ^0.3.23 and was likewise overridden to 0.3.11). Out of scope for this PR; flagging only so a maintainer can confirm the 0.3.11 pin is still intended now that the relay-helpers line has moved to 0.4.x.

Assessment

The diff is coherent, minimal, and verified: typecheck passes, the full test suite passes in a clean environment, deps are in sync, and the persona compiles. No mechanical fixes were required, so nothing was auto-edited. The two findings above are behavior/judgment items (a robustness suggestion and a stale-comment cleanup) for the author to weigh — neither blocks the PR's stated purpose.

I am not printing READY: I cannot observe the actual GitHub CI check states or mergeability from this sandbox (no gh/git access, and the only test failure I saw was a sandbox env artifact). Confirming all required checks are green and the PR is mergeable is a post-harness step; the findings above are advisory and do not require code changes from me.

@agent-relay-code

Copy link
Copy Markdown
Contributor

ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed.

Working tree is clean — I made no edits (none were needed). The .test-build dir is removed by the test script, and node_modules is gitignored.

Review Summary

PR #84 (fix/joke-bot-channel-guard) makes three coherent changes to the joke-bot agent:

  1. Channel guard in handleSlackMention (joke-bot/agent.ts:139-154) — adds a fail-closed check so the bot only replies in the configured SLACK_CHANNEL, normalizing id__nameid via channel.split('__')[0]. This is correct and safety-positive: it preserves fail-closed semantics (returns without replying when SLACK_CHANNEL is unset), matching the relay/cron paths. No callers or types are affected.

  2. sandbox: truefalse (joke-bot/persona.ts:34) — relies on relay-helpers ≥0.4.1 routing writeJsonFile over the RelayFile HTTP API instead of the FS mount, so no Daytona box is needed. This is a deliberate behavior change backed by the dependency bump; the rationale is documented.

  3. @relayfile/relay-helpers 0.3.42 → ^0.4.1 (package.json, package-lock.json) — lockfile is internally consistent (npm ci succeeds; nested adapter-core@0.4.2 recorded; the adapter-linear@0.3.11 override resolves cleanly).

Verification: npm ci ✓, npm run typecheck (tsc --noEmit) ✓, npm test → 125/125 pass in a clean environment.

Addressed comments

  • No bot or human review comments were present (.workforce/ contains only pr.diff, changed-files.txt, and context.json; no comments file). Nothing to address.

Advisory Notes

  • Stale persona doc header (joke-bot/persona.ts:14-19): the file-level comment still states the joke is generated "via ctx.harness.run (NOT ctx.llm.complete)" and references the old sandbox:true rationale, which now contradicts both the updated agent.ts header (lines 10-15) and this PR's sandbox:false + ctx.llm.complete reality. The agent.ts header was updated; persona.ts was not. Left unchanged because it concerns the persona's stated harness-test purpose (human-judgment wording, not a mechanical typo). Recommend a follow-up to align the persona.ts doc comment with the new behavior.
  • Test failure observed in this sandbox only (tests/hn-monitor.test.mjs:111): the test fails here because the sandbox sets SLACK_CHANNEL=C0ALQ06AAUT in the process env, and hn-monitor's input() (hn-monitor/agent.ts:243) prioritizes process.env over the test-injected persona.inputs. This is environment leakage in the sandbox, not a regression from this PR (the PR touches neither hn-monitor/ nor @agentworkforce/runtime). With the env var unset — as in CI — all 125 tests pass. No change made; flagging in case the hn-monitor test's env-precedence brittleness is worth a separate fix.

The PR is internally consistent, typechecks, and its tests pass in a clean environment. No mechanical or semantic fixes were required, so the working tree is unchanged. The remaining items (the sandbox:false behavior change correctness and the advisory doc/test notes) are human-judgment calls, and I cannot confirm the live CI check status or PR mergeability from here.

@agent-relay-code

Copy link
Copy Markdown
Contributor

ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed.

Review: PR #84fix(joke-bot): channel guard + sandbox:false + relay-helpers 0.4.1

Summary of changes

The PR makes three related changes to joke-bot:

  1. Channel guard in handleSlackMention (joke-bot/agent.ts:139-154) — fail-closed if SLACK_CHANNEL unset; skip if the event channel ≠ configured channel.
  2. sandbox: truesandbox: false (joke-bot/persona.ts:34) with a rewritten justification comment, relying on the new HTTP writeback path.
  3. Dependency bump @relayfile/relay-helpers 0.3.42^0.4.1 (package.json, package-lock.json).

Verification performed

  • npm ci — clean (lockfile and package.json are in sync; the overrides block is honored).
  • npm run typecheck (tsc --noEmit) — clean.
  • npm test (full suite, the canonical command) — 125/125 pass under CI-equivalent conditions.
  • One initial failure in tests/hn-monitor.test.mjs was reproduced and traced to a sandbox-only artifact: this reviewer environment exports SLACK_CHANNEL=C0ALQ06AAUT, and input() reads process.env before the persona input, shadowing the test's 'C123'. Unsetting it (as CI does) makes the suite fully green. Not a PR regression; no code or test change made.

No auto-edits were applied — the PR needed no mechanical (lint/format/typo/import) fixes, and the working tree is clean.

Findings (review comments — left for human judgment, not auto-edited)

1. joke-bot/agent.ts:146 — channel normalization is asymmetric. The guard normalizes only the event channel (chanId = channel.split('__')[0]) but compares it against the raw want = input(ctx, 'SLACK_CHANNEL'). If the picker ever resolves SLACK_CHANNEL to an id__name form (the comment on :144 explicitly anticipates that form exists on the event side), the comparison chanId !== want becomes "C123" !== "C123__general" and would silently drop legitimate mentions. This is fail-closed (no safety regression — correctly does not reply to the wrong channel), so I have not changed it. Suggested direction for the author: normalize both sides, e.g. compare chanId against want.split('__')[0]. The sibling agent linear-slack/agent.ts:154 compares raw-to-raw with no split and works, and the persona's own capabilities.channels uses a bare id (C0AD7UU0J1G), so confirm which form SLACK_CHANNEL actually resolves to before changing. Behavioral — needs the author's knowledge of the picker output.

2. sandbox: false is a runtime/architecture change (persona.ts:34). Correctness depends on relay-helpers ≥0.4.1 / adapter-core ≥0.4.2 actually routing writeJsonFile to RelayFileClient (HTTP) when no FS mount exists, using injected RELAYFILE_TOKEN/URL. That path can't be exercised in this sandbox. This is the central purpose of the PR and a human deploy-verification item — flagging rather than touching.

Advisory Notes

  • Stale persona doc-comment (out of scope). joke-bot/persona.ts:14-19 still states the joke is generated via ctx.harness.run (NOT ctx.llm.complete) and "Reply surface is Slack writeback (slackClient().post)", but agent.ts uses ctx.llm.complete. This staleness predates this PR (the diff only touched the sandbox block), so I left it unchanged to avoid folding unrelated cleanup into this PR. Worth a follow-up doc fix.
  • overrides vs. new dep tree (out of scope). package.json:30 pins @relayfile/adapter-linear to 0.3.11, while the new relay-helpers@0.4.1 declares @relayfile/adapter-linear: ^0.4.1 (lock :3941). npm ci resolves cleanly because the override forces 0.3.11, but if relay-helpers 0.4.x depends on adapter-linear 0.4.x API this could surface at runtime for Linear-using agents. joke-bot doesn't use Linear, and revisiting the override touches other packages — out of scope for this PR. Flagging for a maintainer.

Addressed comments

  • No bot or human review comments were available in the harness payload (.workforce/context.json contains only repo/PR/SHA metadata; no reviews/comments fields, and .workforce/ has no comments file). Nothing to reconcile against the current checkout.

The PR is mechanically clean and passes the full build/typecheck/test suite locally, but it carries two behavioral items (the asymmetric channel normalization and the unverifiable sandbox:false HTTP-writeback path) that require author/deployer judgment, and I cannot confirm GitHub merge/CI status from here. I am not printing READY.

khaliqgant added a commit that referenced this pull request Jun 22, 2026
joke-bot, the linear-slack @mention gate, and the relay-helpers ^0.4.1 bump
already landed on main via #83/#84. Resolve the add/add conflicts in favor of
this branch (identical content for all except joke-bot/agent.ts, where this
branch additionally carries the PR #85 review fixes: channel id normalization +
leading-only mention strip). Net-new here remains inbox-buddy + those fixes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant